import { nextTick } from 'vue'
import { isElEditable } from '@/utils'
import { Widget } from '@/models'
import { Viewer } from '@/types'
import HotkeyRegister, { HotkeyHandler } from './HotkeyRegister'
import Clipboard, { ClipboardOperate } from './Clipboard'
import { getWidgetSelection } from './utils'

/** 剪切板内容类型 */
const ClipboardType = 'fdd-contract/widgets'

/** 剪切板数据 */
interface ClipboardValue extends ReturnType<typeof getState> {
  /** 控件列表 */
  widgets: Widget[]
}

/** 获取操作状态 */
function getState(viewer: Viewer) {
  const docId = viewer.doc.id
  const p = viewer.scroll.pageIndex
  const scrollTop = viewer.scroll.state.scrollTop

  return {
    /** 文档 id */
    docId,

    /** 页码 */
    p,

    /** 滚动距离 */
    scrollTop
  }
}

/** 控件复制粘贴 */
export default function useWidgetCopy(register: HotkeyRegister) {
  /** 剪切板 */
  const clipboard = new Clipboard()

  /** 处理复制剪切 */
  const copyOrCutHandler =
    (operate = ClipboardOperate.COPY): HotkeyHandler =>
    ({ e, viewer }) => {
      if (isElEditable(e.target)) return

      let widgets = getWidgetSelection(viewer).filter(w => !w.isFixed && !w.isRiding)

      if (!widgets.length) return

      e.preventDefault()

      /** 剪切操作移除控件 */
      if (operate === ClipboardOperate.CUT) {
        // 批量控件不支持剪切
        widgets = widgets.filter(w => !w.isBatch)
        viewer.removeWidgets(widgets)
      }

      clipboard.set({
        type: ClipboardType,
        value: {
          widgets,
          ...getState(viewer)
        } as ClipboardValue,
        operate
      })
    }

  /**
   * 复制控件
   */
  register.add({
    keys: 'C',
    ctrlOrCmd: true,
    interval: 500,
    handler: copyOrCutHandler()
  })

  /**
   * 剪切控件
   */
  register.add({
    keys: 'X',
    ctrlOrCmd: true,
    interval: 500,
    handler: copyOrCutHandler(ClipboardOperate.CUT)
  })

  /**
   * 粘贴剪切板控件
   */
  register.add({
    keys: 'V',
    ctrlOrCmd: true,
    interval: 300,
    handler: async ({ e, viewer }) => {
      /** 剪切板数据 */
      const data = clipboard.get()

      if (data?.type !== ClipboardType || isElEditable(document.activeElement)) return

      e.preventDefault()

      const { selection } = viewer

      // 清理选择和剪切板
      viewer.activeWidget = undefined

      selection.clear()

      clipboard.clear()

      const { type, operate } = data

      const value = data.value as ClipboardValue

      const { docId, p, scrollTop } = getState(viewer)

      let { widgets } = value

      const isCopy = operate === ClipboardOperate.COPY

      /** 是否更改了页面 */
      const pageChanged = docId !== value.docId || p !== value.p

      // 批量控件不允许复制和剪切到其他页
      if (pageChanged) widgets = widgets.filter(w => !w.isBatch)

      // 复制
      if (isCopy) {
        // 克隆粘贴的控件
        widgets = widgets.map(w => {
          const clone = w.clone()

          // 相同页偏移避免覆盖
          !pageChanged && clone.move(10, 10)

          return clone
        })

        // 存放已粘贴的控件到粘贴板，便于下次粘贴相对定位
        clipboard.set({
          type,
          value: { widgets, docId, p, scrollTop },
          operate
        })
      }

      // 更改页码和页面
      if (pageChanged) {
        widgets.forEach(w =>
          Object.assign(w, {
            docId,
            p
          })
        )
      }

      // 添加控件
      viewer.addWidget(...widgets)

      // 添加到选择
      selection.add(...widgets)

      /** 是否滚动 */
      const scrolled = Math.abs(scrollTop - value.scrollTop) > 50

      // 如果页面改变或者滚动距离过远则滚动到第一个控件
      if (pageChanged || scrolled) {
        await nextTick()

        viewer.scroll.scrollToWidget(widgets[0].id)
      }
    }
  })
}
